<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>化学方程式配平工具</title>
  <!-- 引入Tailwind CSS -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- 引入Font Awesome -->
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">

  <!-- 配置Tailwind自定义颜色和字体 -->
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3B82F6',
            secondary: '#10B981',
            neutral: '#F3F4F6',
            dark: '#1F2937'
          },
          fontFamily: {
            sans: ['Inter', 'system-ui', 'sans-serif'],
            mono: ['Fira Code', 'monospace']
          }
        }
      }
    }
  </script>

  <!-- 自定义工具类 -->
  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto {
        content-visibility: auto;
      }
      .equation-shadow {
        box-shadow: 0 4px 20px rgba(59, 130, 246, 0.15);
      }
      .btn-hover {
        transition: all 0.3s ease;
      }
      .btn-hover:hover {
        transform: translateY(-2px);
      }
    }
  </style>
</head>
<body class="bg-gradient-to-br from-slate-50 to-slate-100 min-h-screen font-sans">
<!-- 页面容器 -->
<div class="container mx-auto px-4 py-8 max-w-4xl">
  <!-- 页面标题 -->
  <header class="text-center mb-8">
    <h1 class="text-[clamp(2rem,5vw,3rem)] font-bold text-dark mb-2">
      <i class="fa fa-flask text-primary mr-2"></i>化学方程式配平工具
    </h1>
    <p class="text-slate-600 max-w-2xl mx-auto">
      输入未配平的化学方程式，如 <span class="font-mono bg-neutral px-2 py-1 rounded">H2+O2=H2O</span>，点击配平按钮获取配平结果
    </p>
  </header>

  <!-- 主要内容区 -->
  <main class="bg-white rounded-xl p-6 md:p-8 equation-shadow mb-8">
    <!-- 输入区域 -->
    <div class="mb-6">
      <label for="equationInput" class="block text-lg font-medium text-slate-700 mb-2">
        输入未配平的化学方程式
      </label>
      <div class="relative">
        <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
          <i class="fa fa-flask text-primary"></i>
        </div>
        <input
                type="text"
                id="equationInput"
                placeholder="例如: H2+O2=H2O"
                class="block w-full pl-10 pr-4 py-3 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary transition-all"
        >
      </div>
      <p id="inputHint" class="text-sm text-slate-500 mt-1 hidden">
        <i class="fa fa-info-circle mr-1"></i>
        支持的格式：使用 + 连接反应物和产物，使用 = 或 -> 表示反应方向
      </p>
    </div>

    <!-- 操作按钮 -->
    <div class="flex flex-wrap gap-4 mb-8">
      <button
              id="balanceButton"
              class="flex-1 bg-primary hover:bg-primary/90 text-white font-medium py-3 px-6 rounded-lg shadow-md btn-hover flex items-center justify-center"
      >
        <i class="fa fa-balance-scale mr-2"></i>
        配平方程式
      </button>
      <button
              id="clearButton"
              class="flex-1 bg-slate-200 hover:bg-slate-300 text-slate-700 font-medium py-3 px-6 rounded-lg shadow-md btn-hover flex items-center justify-center"
      >
        <i class="fa fa-eraser mr-2"></i>
        清除输入
      </button>
    </div>

    <!-- 结果展示区域 -->
    <div id="resultContainer" class="mt-8 border-t border-slate-200 pt-6 hidden">
      <h2 class="text-xl font-semibold text-slate-800 mb-4 flex items-center">
        <i class="fa fa-check-circle text-secondary mr-2"></i>
        配平结果
      </h2>
      <div id="resultArea" class="bg-neutral p-4 rounded-lg font-mono text-lg overflow-x-auto">
        <!-- 结果将在这里显示 -->
      </div>
      <div id="errorArea" class="bg-red-50 border border-red-200 text-red-700 p-4 rounded-lg mt-4 hidden">
        <div class="flex">
          <div class="flex-shrink-0">
            <i class="fa fa-exclamation-triangle text-red-500"></i>
          </div>
          <div class="ml-3">
            <h3 class="text-sm font-medium text-red-800">配平失败</h3>
            <div class="mt-2 text-sm text-red-700" id="errorMessage">
              <!-- 错误信息将在这里显示 -->
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 加载指示器 -->
    <div id="loadingIndicator" class="flex justify-center items-center py-6 hidden">
      <div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div>
    </div>
  </main>



</div>

<!-- 引入您的配平方程式代码 -->
<script>
  // 这里是您提供的JavaScript代码
  let $ = {
    math: {
      sum: arr => arr.reduce((a, b) => a + b, 0),
      mat: {
        mul: (a, b) => {
          const rowsA = a.length;
          const colsA = a[0].length;
          const rowsB = b.length;
          const colsB = b[0].length;
          if (colsA !== rowsB) throw new Error("矩阵维度不匹配");
          const result = Array(rowsA).fill().map(() => Array(colsB).fill(0));
          for (let i = 0; i < rowsA; i++) {
            for (let j = 0; j < colsB; j++) {
              for (let k = 0; k < colsA; k++) {
                result[i][j] += a[i][k] * b[k][j];
              }
            }
          }
          return result;
        },
        transpose: a => a[0].map((_, colIndex) => a.map(row => row[colIndex]))
      },
      gaussian: matrix => {
        // 简化版高斯消元法
        const n = matrix.length;
        const m = matrix[0].length - 1;
        const result = Array(m).fill(0);

        for (let i = 0; i < n; i++) {
          let maxRow = i;
          for (let j = i + 1; j < n; j++) {
            if (Math.abs(matrix[j][i]) > Math.abs(matrix[maxRow][i])) {
              maxRow = j;
            }
          }

          const temp = matrix[i];
          matrix[i] = matrix[maxRow];
          matrix[maxRow] = temp;

          if (Math.abs(matrix[i][i]) < 1e-10) continue;

          for (let j = i + 1; j < n; j++) {
            const factor = matrix[j][i] / matrix[i][i];
            for (let k = i; k < m + 1; k++) {
              matrix[j][k] -= factor * matrix[i][k];
            }
          }
        }

        for (let i = n - 1; i >= 0; i--) {
          if (Math.abs(matrix[i][i]) < 1e-10) continue;

          result[i] = matrix[i][m] / matrix[i][i];

          for (let j = i - 1; j >= 0; j--) {
            matrix[j][m] -= matrix[j][i] * result[i];
            matrix[j][i] = 0;
          }
        }

        return result;
      },
      approximatelyEqual: (a, b, epsilon = 1e-10) => Math.abs(a - b) < epsilon,
      round: (num, decimalPlaces = 0) => {
        const factor = 10 ** decimalPlaces;
        return Math.round(num * factor) / factor;
      }
    },
    c: {
      y: text => `<span class="text-yellow-600">${text}</span>`
    },
    // 扩展数组方法
    Array: {
      remove: (arr, from, to) => {
        const rest = arr.slice((to || from) + 1 || arr.length);
        arr.length = from < 0 ? arr.length + from : from;
        return arr.push.apply(arr, rest);
      }
    }
  };

  // 扩展数组原型方法
  if (!Array.prototype.remove) {
    Array.prototype.remove = function(from, to) {
      const rest = this.slice((to || from) + 1 || this.length);
      this.length = from < 0 ? this.length + from : from;
      return this.push.apply(this, rest);
    };
  }

  if (!Array.prototype.copy) {
    Array.prototype.copy = function() {
      return JSON.parse(JSON.stringify(this));
    };
  }

  let chemSymbol =
    'H,He,Li,Be,B,C,N,O,F,Ne,Na,Mg,Al,Si,P,S,Cl,Ar,K,Ca,Sc,Ti,V,Cr,Mn,Fe,Co,Ni,Cu,Zn,Ga,Ge,As,Se,Br,Kr,Rb,Sr,Y,Zr,Nb,Mo,Tc,Ru,Rh,Pd,Ag,Cd,In,Sn,Sb,Te,I,Xe,Cs,Ba,La,Ce,Pr,Nd,Pm,Sm,Eu,Gd,Tb,Dy,Ho,Er,Tm,Yb,Lu,Hf,Ta,W,Re,Os,Ir,Pt,Au,Hg,Tl,Pb,Bi,Po,At,Rn,Fr,Ra,Ac,Th,Pa,U,Np,Pu,Am,Cm,Bk,Cf,Es,Fm,Md,No,Lr,Rf,Db,Sg,Bh,Hs,Mt,Ds,Rg,Cn,Nh,Fl,Mc,Lv,Ts,Og'.split(',');

  function expandElm(elm) {
    const regex = /\(([^)]+)\)(\d*)/;
    return elm
      .replace(regex, (match, group, count) => {
        count = +count || 1; // default 1
        return group.repeat(count);
      })
      .replace(/\(|\)/g, ''); // remove '(' ,')'
  }

  function chemEq2Matrix(chemStr) {
    let eqArr = chemStr.replace(/ +/g, '').split(/(\->|=|\-|→)/g);
    let leftEqArrOri = eqArr[0].split('+'); // unexpand element
    let rightEqArrOri = eqArr[2].split('+');

    let leftEqArr = leftEqArrOri.map(x => expandElm(x));
    let rightEqArr = rightEqArrOri.map(x => expandElm(x));

    let chemElmObj = {}; // element inside 有哪些元素
    eqArr = [...leftEqArr, ...rightEqArr];
    let len = chemSymbol.length;
    let leftLen = leftEqArr.length;
    let rightLen = rightEqArr.length;
    let matrix = [[1, ...Array(leftLen + rightLen).fill(0)]];

    for (let i = 0; i < len; i++) {
      let chemElm = chemSymbol[i];
      let rowArr = [];
      let idx = 0;
      for (let n = 0; n < leftLen + rightLen; n++) {
        let isElm =
          eqArr[n].match(
            new RegExp(chemElm + '[0-9]*(?=[A-Z]+|[0-9]+|$)', 'g')
          ) || []; // 同一个式子里面有多个 同样的元素
        if (isElm.length > 0) {
          let sum = $.math.sum(isElm.map(x => +x.replace(chemElm, '') || 1));
          rowArr[idx] = (idx >= leftLen ? -1 : 1) * sum;
          chemElmObj[chemElm] = 1;
        } else {
          rowArr[idx] = 0;
        }
        idx++;
      }
      if (rowArr.some(x => x != 0)) {
        rowArr.push(0);
        matrix.push(rowArr);
      }
    }

    if (matrix[0].length <= matrix.length) {
      //over-determined equation 解决超定方程
      matrix.remove(0);
      for (let i = 0; i < matrix.length; i++) {
        matrix[i].remove(matrix[i].length - 1);
      }
      matrix = $.math.mat.mul($.math.mat.transpose(matrix), matrix);
      for (let i = 0; i < matrix.length; i++) {
        matrix[i].push(0);
      }
      matrix.unshift([1, ...Array(leftLen + rightLen).fill(0)]); //重新填入值

      // 方程组大于未知数 移除有第一个主元的一行
      for (let i = 1; i < matrix.length; i++) {
        if (matrix[i][0]) {
          matrix.remove(i, 1);
          break;
        }
      }
    }

    return {
      matrix,
      leftEqArr,
      rightEqArr,
      leftEqArrOri,
      rightEqArrOri,
      leftLen,
      rightLen,
      chemElmObj
    };
  }

  function balanceEq(chemStr) {
    try {
      // render formula
      let m = chemEq2Matrix(chemStr);
      m.leftEqArrColor = [];
      m.rightEqArrColor = [];
      let pos = m.matrix[0].length - 1;
      for (let i = 0; i < 200; i++) {
        let newMatrix = m.matrix.copy();
        newMatrix[0][pos] = i + 1;
        let rst = $.math.gaussian(newMatrix);
        if (
          rst.every(x => {
            return $.math.approximatelyEqual(x, $.math.round(x, 0) | 0);
          })
        ) {
          let newEqArr = [];
          let ratio;
          for (let i = 0; i < m.leftLen + m.rightLen; i++) {
            ratio = $.math.round(rst[i], 0);
            if (i < m.leftLen) {
              m.leftEqArrColor[i] = $.c.y(ratio) + m.leftEqArrOri[i];
              m.leftEqArr[i] = (ratio === 1 ? '' : ratio) + m.leftEqArrOri[i];
            } else {
              m.rightEqArrColor[i - m.leftLen] =
                $.c.y(ratio) + m.rightEqArrOri[i - m.leftLen];
              m.rightEqArr[i - m.leftLen] =
                (ratio === 1 ? '' : ratio) + m.rightEqArrOri[i - m.leftLen];
            }
          }
          let outChemColorStr =
            m.leftEqArrColor.join(' + ') + ' = ' + m.rightEqArrColor.join(' + ');
          let outChemStr =
            m.leftEqArr.join(' + ') + ' = ' + m.rightEqArr.join(' + ');
            let params = {
              name: outChemStr,
              success: function (res) {
                    console.log("luviWeb > openNativePage success. " + res)
                },
              fail: function () {
                  console.log("luviWeb > openNativePage fail.")
                }
            }
            window.JSBridge.nativeMethod("balanceEq", params)

          return {
            oriChem: chemStr,
            outChem: outChemStr,
            outChemCol: outChemColorStr
          };
        }
      }
      return { oriChem: chemStr, outChem: '', outChemCol: '' };
    } catch (e) {
      console.error('配平错误:', e);
      return { oriChem: chemStr, outChem: '', outChemCol: '' };
    }
  }

</script>

<!-- 页面交互逻辑 -->
<script>
  document.addEventListener('DOMContentLoaded', () => {
    // 获取DOM元素
    const equationInput = document.getElementById('equationInput');
    const balanceButton = document.getElementById('balanceButton');
    const clearButton = document.getElementById('clearButton');
    const resultContainer = document.getElementById('resultContainer');
    const resultArea = document.getElementById('resultArea');
    const errorArea = document.getElementById('errorArea');
    const errorMessage = document.getElementById('errorMessage');
    const loadingIndicator = document.getElementById('loadingIndicator');
    const inputHint = document.getElementById('inputHint');
    const exampleEquations = document.querySelectorAll('[data-equation]');


    // 显示输入提示
    equationInput.addEventListener('focus', () => {
      inputHint.classList.remove('hidden');
    });

    equationInput.addEventListener('blur', () => {
      if (equationInput.value.trim() === '') {
        inputHint.classList.add('hidden');
      }
    });

    // 清除输入
    clearButton.addEventListener('click', () => {
      equationInput.value = '';
      resultContainer.classList.add('hidden');
      inputHint.classList.add('hidden');
      equationInput.focus();
    });

    // 使用示例方程式
    exampleEquations.forEach(example => {
      example.addEventListener('click', () => {
        const equation = example.getAttribute('data-equation');
        equationInput.value = equation;
        balanceEquation();
      });
    });

    // 配平方程式
    balanceButton.addEventListener('click', balanceEquation);

    // 支持按Enter键配平
    equationInput.addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        balanceEquation();
      }
    });

    // 配平方程式的主函数
    function balanceEquation() {
      const equation = equationInput.value.trim();

      // 验证输入
      if (!equation) {
        showError('请输入化学方程式');
        return;
      }

      // 检查输入格式
      if (!equation.match(/[A-Za-z0-9]+\s*[\+\-\=→]\s*[A-Za-z0-9]+/)) {
        showError('方程式格式不正确，请检查');
        return;
      }

      // 显示加载状态
      resultContainer.classList.add('hidden');
      errorArea.classList.add('hidden');
      loadingIndicator.classList.remove('hidden');

      // 模拟处理延迟
      setTimeout(() => {
        try {
          // 调用配平方程式函数
          const result = balanceEq(equation);

          // 隐藏加载状态
          loadingIndicator.classList.add('hidden');

          if (result.outChem) {
            // 显示成功结果
            resultArea.innerHTML = result.outChemCol;
            resultContainer.classList.remove('hidden');
            errorArea.classList.add('hidden');

            // 添加动画效果
            resultContainer.classList.add('animate-fadeIn');
          } else {
            // 显示错误
            showError('无法配平此方程式，请检查格式或元素符号');
          }
        } catch (error) {
          // 隐藏加载状态
          loadingIndicator.classList.add('hidden');

          // 显示错误
          showError('处理方程式时发生错误: ' + error.message);
        }
      }, 500); // 模拟处理延迟
    }

    // 显示错误信息
    function showError(message) {
      errorMessage.textContent = message;
      errorArea.classList.remove('hidden');
      resultContainer.classList.add('hidden');
      loadingIndicator.classList.add('hidden');

      // 添加错误动画
      errorArea.classList.add('animate-shake');
      setTimeout(() => {
        errorArea.classList.remove('animate-shake');
      }, 500);
    }

    // 添加动画类
    const style = document.createElement('style');
    style.textContent = `
      @keyframes fadeIn {
        from { opacity: 0; transform: translateY(10px); }
        to { opacity: 1; transform: translateY(0); }
      }

      @keyframes shake {
        0%, 100% { transform: translateX(0); }
        10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
        20%, 40%, 60%, 80% { transform: translateX(5px); }
      }

      .animate-fadeIn {
        animation: fadeIn 0.5s ease-out forwards;
      }

      .animate-shake {
        animation: shake 0.5s ease-in-out;
      }
    `;
    document.head.appendChild(style);
  });
</script>
</body>
</html>
